/*
===========================================================================

Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.

This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").

Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.

If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.

===========================================================================
*/

#include "Precompiled.h"
#include "globaldata.h"


#include "doomdef.h"

#include "m_bbox.h"

#include "i_system.h"

#include "r_main.h"
#include "r_plane.h"
#include "r_things.h"

// State.
#include "doomstat.h"
#include "r_state.h"

//#include "r_local.h"






void
R_StoreWallRange
( int	start,
  int	stop );




//
// R_ClearDrawSegs
//
void R_ClearDrawSegs( void )
{
	::g->ds_p = ::g->drawsegs;
}



//
// ClipWallSegment
// Clips the given range of columns
// and includes it in the new clip list.
//



// ::g->newend is one past the last valid seg




//
// R_ClipSolidWallSegment
// Does handle solid walls,
//  e.g. single sided LineDefs (middle texture)
//  that entirely block the view.
//
void
R_ClipSolidWallSegment
( int			first,
  int			last )
{
	cliprange_t*	next;
	cliprange_t*	start;

	// Find the first range that touches the range
	//  (adjacent pixels are touching).
	start = ::g->solidsegs;
	while( start->last < first - 1 )
	{
		start++;
	}

	if( first < start->first )
	{
		if( last < start->first - 1 )
		{
			// Post is entirely visible (above start),
			//  so insert a new clippost.
			R_StoreWallRange( first, last );
			next = ::g->newend;
			::g->newend++;

			while( next != start )
			{
				*next = *( next - 1 );
				next--;
			}
			next->first = first;
			next->last = last;
			return;
		}

		// There is a fragment above *start.
		R_StoreWallRange( first, start->first - 1 );
		// Now adjust the clip size.
		start->first = first;
	}

	// Bottom contained in start?
	if( last <= start->last )
	{
		return;
	}

	next = start;
	while( last >= ( next + 1 )->first - 1 )
	{
		// There is a fragment between two posts.
		R_StoreWallRange( next->last + 1, ( next + 1 )->first - 1 );
		next++;

		if( last <= next->last )
		{
			// Bottom is contained in next.
			// Adjust the clip size.
			start->last = next->last;
			goto crunch;
		}
	}

	// There is a fragment after *next.
	R_StoreWallRange( next->last + 1, last );
	// Adjust the clip size.
	start->last = last;

	// Remove start+1 to next from the clip list,
	// because start now covers their area.
crunch:
	if( next == start )
	{
		// Post just extended past the bottom of one post.
		return;
	}


	while( next++ != ::g->newend )
	{
		// Remove a post.
		*++start = *next;
	}

	::g->newend = start + 1;
}



//
// R_ClipPassWallSegment
// Clips the given range of columns,
//  but does not includes it in the clip list.
// Does handle windows,
//  e.g. LineDefs with upper and lower texture.
//
void
R_ClipPassWallSegment
( int	first,
  int	last )
{
	cliprange_t*	start;

	// Find the first range that touches the range
	//  (adjacent pixels are touching).
	start = ::g->solidsegs;
	while( start->last < first - 1 )
	{
		start++;
	}

	if( first < start->first )
	{
		if( last < start->first - 1 )
		{
			// Post is entirely visible (above start).
			R_StoreWallRange( first, last );
			return;
		}

		// There is a fragment above *start.
		R_StoreWallRange( first, start->first - 1 );
	}

	// Bottom contained in start?
	if( last <= start->last )
	{
		return;
	}

	while( last >= ( start + 1 )->first - 1 )
	{
		// There is a fragment between two posts.
		R_StoreWallRange( start->last + 1, ( start + 1 )->first - 1 );
		start++;

		if( last <= start->last )
		{
			return;
		}
	}

	// There is a fragment after *next.
	R_StoreWallRange( start->last + 1, last );
}



//
// R_ClearClipSegs
//
void R_ClearClipSegs( void )
{
	::g->solidsegs[0].first = -0x7fffffff;
	::g->solidsegs[0].last = -1;
	::g->solidsegs[1].first = ::g->viewwidth;
	::g->solidsegs[1].last = 0x7fffffff;
	::g->newend = ::g->solidsegs + 2;
}

//
// R_AddLine
// Clips the given segment
// and adds any visible pieces to the line list.
//
void R_AddLine( seg_t*	line )
{
	int			x1;
	int			x2;
	angle_t		angle1;
	angle_t		angle2;
	angle_t		span;
	angle_t		tspan;

	::g->curline = line;

	// OPTIMIZE: quickly reject orthogonal back ::g->sides.
	angle1 = R_PointToAngle( line->v1->x, line->v1->y );
	angle2 = R_PointToAngle( line->v2->x, line->v2->y );

	// Clip to view edges.
	// OPTIMIZE: make constant out of 2*::g->clipangle (FIELDOFVIEW).
	span = angle1 - angle2;

	// Back side? I.e. backface culling?
	if( span >= ANG180 )
	{
		return;
	}

	extern angle_t GetViewAngle();
	// Global angle needed by segcalc.
	::g->rw_angle1 = angle1;
	angle1 -= GetViewAngle();
	angle2 -= GetViewAngle();

	tspan = angle1 + ::g->clipangle;
	if( tspan > 2 *::g->clipangle )
	{
		tspan -= 2 *::g->clipangle;

		// Totally off the left edge?
		if( tspan >= span )
		{
			return;
		}

		angle1 = ::g->clipangle;
	}
	tspan = ::g->clipangle - angle2;
	if( tspan > 2 *::g->clipangle )
	{
		tspan -= 2 *::g->clipangle;

		// Totally off the left edge?
		if( tspan >= span )
		{
			return;
		}
		angle2 = UINT_MAX - ::g->clipangle + 1; // ALANHACK UNSIGNED, SRS - make uint math explicit
	}

	// The seg is in the view range,
	// but not necessarily visible.
	angle1 = ( angle1 + ANG90 ) >> ANGLETOFINESHIFT;
	angle2 = ( angle2 + ANG90 ) >> ANGLETOFINESHIFT;
	x1 = ::g->viewangletox[angle1];
	x2 = ::g->viewangletox[angle2];

	// Does not cross a pixel?
	if( x1 == x2 )
	{
		return;
	}

	::g->backsector = line->backsector;

	// Single sided line?
	if( !::g->backsector )
	{
		goto clipsolid;
	}

	// Closed door.
	if( ::g->backsector->ceilingheight <= ::g->frontsector->floorheight
			|| ::g->backsector->floorheight >= ::g->frontsector->ceilingheight )
	{
		goto clipsolid;
	}

	// Window.
	if( ::g->backsector->ceilingheight != ::g->frontsector->ceilingheight
			|| ::g->backsector->floorheight != ::g->frontsector->floorheight )
	{
		goto clippass;
	}

	// Reject empty ::g->lines used for triggers
	//  and special ::g->events.
	// Identical floor and ceiling on both ::g->sides,
	// identical light levels on both ::g->sides,
	// and no middle texture.
	if( ::g->backsector->ceilingpic == ::g->frontsector->ceilingpic
			&& ::g->backsector->floorpic == ::g->frontsector->floorpic
			&& ::g->backsector->lightlevel == ::g->frontsector->lightlevel
			&& ::g->curline->sidedef->midtexture == 0 )
	{
		return;
	}


clippass:
	R_ClipPassWallSegment( x1, x2 - 1 );
	return;

clipsolid:
	R_ClipSolidWallSegment( x1, x2 - 1 );
}


//
// R_CheckBBox
// Checks BSP node/subtree bounding box.
// Returns true
//  if some part of the bbox might be visible.
//


qboolean R_CheckBBox( fixed_t*	bspcoord )
{
	int			boxx;
	int			boxy;
	int			boxpos;

	fixed_t		x1;
	fixed_t		y1;
	fixed_t		x2;
	fixed_t		y2;

	angle_t		angle1;
	angle_t		angle2;
	angle_t		span;
	angle_t		tspan;

	cliprange_t*	start;

	int			sx1;
	int			sx2;

	extern fixed_t GetViewX();
	extern fixed_t GetViewY();
	// Find the corners of the box
	// that define the edges from current viewpoint.
	if( GetViewX() <= bspcoord[BOXLEFT] )
	{
		boxx = 0;
	}
	else if( GetViewX() < bspcoord[BOXRIGHT] )
	{
		boxx = 1;
	}
	else
	{
		boxx = 2;
	}

	if( GetViewY() >= bspcoord[BOXTOP] )
	{
		boxy = 0;
	}
	else if( GetViewY() > bspcoord[BOXBOTTOM] )
	{
		boxy = 1;
	}
	else
	{
		boxy = 2;
	}

	boxpos = ( boxy << 2 ) + boxx;
	if( boxpos == 5 )
	{
		return true;
	}

	x1 = bspcoord[::g->checkcoord[boxpos][0]];
	y1 = bspcoord[::g->checkcoord[boxpos][1]];
	x2 = bspcoord[::g->checkcoord[boxpos][2]];
	y2 = bspcoord[::g->checkcoord[boxpos][3]];

	// check clip list for an open space
	extern angle_t GetViewAngle();
	angle1 = R_PointToAngle( x1, y1 ) - GetViewAngle();
	angle2 = R_PointToAngle( x2, y2 ) - GetViewAngle();

	span = angle1 - angle2;

	// Sitting on a line?
	if( span >= ANG180 )
	{
		return true;
	}

	tspan = angle1 + ::g->clipangle;

	if( tspan > 2 *::g->clipangle )
	{
		tspan -= 2 *::g->clipangle;

		// Totally off the left edge?
		if( tspan >= span )
		{
			return false;
		}

		angle1 = ::g->clipangle;
	}
	tspan = ::g->clipangle - angle2;
	if( tspan > 2 *::g->clipangle )
	{
		tspan -= 2 *::g->clipangle;

		// Totally off the left edge?
		if( tspan >= span )
		{
			return false;
		}

		angle2 = UINT_MAX - ::g->clipangle + 1;	// ALANHACK UNSIGNED, SRS - make uint math explicit
	}


	// Find the first clippost
	//  that touches the source post
	//  (adjacent pixels are touching).
	angle1 = ( angle1 + ANG90 ) >> ANGLETOFINESHIFT;
	angle2 = ( angle2 + ANG90 ) >> ANGLETOFINESHIFT;
	sx1 = ::g->viewangletox[angle1];
	sx2 = ::g->viewangletox[angle2];

	// Does not cross a pixel.
	if( sx1 == sx2 )
	{
		return false;
	}
	sx2--;

	start = ::g->solidsegs;
	while( start->last < sx2 )
	{
		start++;
	}

	if( sx1 >= start->first
			&& sx2 <= start->last )
	{
		// The clippost contains the new span.
		return false;
	}

	return true;
}



//
// R_Subsector
// Determine floor/ceiling planes.
// Add ::g->sprites of things in sector.
// Draw one or more line segments.
//
void R_Subsector( int num )
{
	int			count;
	seg_t*		line;
	subsector_t*	sub;

#ifdef RANGECHECK
	if( num >=::g->numsubsectors )
		I_Error( "R_Subsector: ss %i with numss = %i",
				 num,
				 ::g->numsubsectors );
#endif

	::g->sscount++;
	sub = &::g->subsectors[num];
	::g->frontsector = sub->sector;
	count = sub->numlines;
	line = &::g->segs[sub->firstline];

	if( ::g->frontsector->floorheight < ::g->viewz )
	{
		::g->floorplane = R_FindPlane( ::g->frontsector->floorheight,
									   ::g->frontsector->floorpic,
									   ::g->frontsector->lightlevel );
	}
	else
	{
		::g->floorplane = NULL;
	}

	if( ::g->frontsector->ceilingheight > ::g->viewz
			|| ::g->frontsector->ceilingpic == ::g->skyflatnum )
	{
		::g->ceilingplane = R_FindPlane( ::g->frontsector->ceilingheight,
										 ::g->frontsector->ceilingpic,
										 ::g->frontsector->lightlevel );
	}
	else
	{
		::g->ceilingplane = NULL;
	}

	R_AddSprites( ::g->frontsector );

	while( count-- )
	{
		R_AddLine( line );
		line++;
	}
}




//
// RenderBSPNode
// Renders all ::g->subsectors below a given node,
//  traversing subtree recursively.
// Just call with BSP root.
void R_RenderBSPNode( int bspnum )
{
	node_t*	bsp;
	int		side;

	// Found a subsector?
	if( bspnum & NF_SUBSECTOR )
	{
		if( bspnum == -1 )
		{
			R_Subsector( 0 );
		}
		else
		{
			R_Subsector( bspnum & ( ~NF_SUBSECTOR ) );
		}
		return;
	}

	bsp = &::g->nodes[bspnum];

	extern fixed_t GetViewX();
	extern fixed_t GetViewY();
	// Decide which side the view point is on.
	side = R_PointOnSide( GetViewX(), GetViewY(), bsp );

	// Recursively divide front space.
	R_RenderBSPNode( bsp->children[side] );

	// Possibly divide back space.
	if( R_CheckBBox( bsp->bbox[side ^ 1] ) )
	{
		R_RenderBSPNode( bsp->children[side ^ 1] );
	}
}



